package com.lee.auth.sms;

import com.lee.common.exception.CustomException;
import com.lee.common.exception.FailureStatus;
import io.lettuce.core.api.sync.RedisCommands;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 短信发送相关服务
 *
 * @author Lee HN
 * @date 2020/7/21 16:44
 */
@Service
@AllArgsConstructor
public class SmsService {

    private final RedisCommands<String, String> redisCommands;

    private final SmsSenderHolder smsSenderHolder;

    private static final String SMS_INTERVAL = "sms:interval:";

    private static final long SEND_INTERVAL = 58L;

    private static final String SMS_PREFIX = "sms:%s:%s";

    private static final long SMS_CODE_DURATION = 300L;

    private static final Map<String, Boolean> registeredSmsMap;

    static {
        registeredSmsMap = new HashMap<>();
        for (SmsScene s : SmsScene.values()) {
            registeredSmsMap.put(s.name(), s.registered);
        }
    }

    public void sendSms(String phone, String scene, Map<String, Object> param) {
        SmsScene smsScene = verifyScene(phone, scene);
        sendInterval(phone);
        smsSenderHolder.sendSms(phone, smsScene, param);
    }

    public void sendSmsCode(String phone, String scene, String smsCode) {
        SmsScene smsScene = verifyScene(phone, scene);
        sendInterval(phone);
        redisCommands.setex(String.format(SMS_PREFIX, scene, phone), SMS_CODE_DURATION, smsCode);
        smsSenderHolder.sendSms(phone, smsScene, Collections.singletonMap("code", smsCode));
    }

    private static final CustomException SceneFailed = new CustomException(FailureStatus.BAD_OPERATION, "scene failed");

    public final SmsScene verifyScene(String phone, String scene) {
        SmsScene smsScene;
        try {
            smsScene = SmsScene.valueOf(scene);
        } catch (IllegalArgumentException e) {
            throw SceneFailed;
        }
        /*Boolean registered = registeredSmsMap.get(scene);
        if (registered != null) {
            boolean exists = clientUserRepository.existsByAccount(phone);
            if (registered != exists)
                throw new UnsupportedOperationException("手机号 " + phone + (exists ? " 已注册" : " 未注册"));
        }*/
        return smsScene;
    }

    private static final CustomException TooManyRequests = new CustomException(FailureStatus.BAD_OPERATION, "Too many requests");

    public final void sendInterval(String phone) {
        String key = SMS_INTERVAL + phone;
        String cache = redisCommands.get(key);
        if (cache == null) redisCommands.setex(key, SEND_INTERVAL, phone);
        else throw TooManyRequests;
    }

    private static final CustomException CodeNonentity = new CustomException(FailureStatus.BAD_CREDENTIAL, "Code nonentity");

    private static final CustomException CodeFailed = new CustomException(FailureStatus.BAD_CREDENTIAL, "Code failed");

    public void verifyCode(String phone, String scene, String smsCode) {
        String key = String.format(SMS_PREFIX, scene, phone);
        String cacheCode = redisCommands.get(key);
        if (cacheCode == null) throw CodeNonentity;
        if (!Objects.equals(cacheCode, smsCode)) throw CodeFailed;
        redisCommands.del(key);
    }

}
